package tools

import (
	"database/sql/driver"
	"fmt"
	"regexp"
	"strings"
	sysTime "time"
)

type Time struct {
	sysTime.Time
	layout string
}

var TimeLayout = "2006-01-02 15:04:05"
var TimeZone = sysTime.Local
var TimeLayouts = map[string]*regexp.Regexp{
	"2006-01-02 15:04:05": regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`),
	"2006-01-02 15:04":    regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$`),
	"2006-01-02 15":       regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}$`),
	"2006-01-02":          regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`),
	"2006-01":             regexp.MustCompile(`^\d{4}-\d{2}$`),
	"2006":                regexp.MustCompile(`^\d{4}$`),
}

// 在使用 json.Marshal 时触发
func (t Time) MarshalJSON() ([]byte, error) {
	if t.IsZero() {
		return []byte("null"), nil
	}
	if t.layout == "" {
		return []byte(fmt.Sprintf(`"%s"`, t.Format(TimeLayout))), nil
	} else {
		return []byte(fmt.Sprintf(`"%s"`, t.Format(t.layout))), nil
	}
}

// 在使用 json.Unmarshal 时触发
func (t *Time) UnmarshalJSON(data []byte) error {
	timeStr := string(data)
	// null 或者 \"\" 被认为时间的零值
	if timeStr == "null" || timeStr == "\"\"" {
		// zero time
		*t = Time{}
		return nil
	}
	timeStr = strings.Trim(timeStr, "\"")
	for format, pattern := range TimeLayouts {
		if pattern.MatchString(timeStr) {
			t.layout = format
			break
		}
	}
	if t.layout == "" {
		t.layout = TimeLayout
	}
	timeStr = `"` + timeStr + `"`
	st, err := sysTime.ParseInLocation(`"`+t.layout+`"`, timeStr, TimeZone)
	if err != nil {
		return err
	}
	*t = Time{Time: st}
	return nil
}

func (t Time) Value() (driver.Value, error) {
	var zeroTime sysTime.Time
	if t.Time.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return t.Time, nil
}

func (t *Time) Scan(v interface{}) error {
	value, ok := v.(sysTime.Time)
	if ok {
		*t = Time{Time: value}
		return nil
	}
	return fmt.Errorf("can not convert %v to timestamp", v)
}

// fmt.Printf 触发
func (t *Time) String() string {
	if t.layout == "" {
		return fmt.Sprintf(`"%s"`, t.Format(TimeLayout))
	} else {
		return fmt.Sprintf(`"%s"`, t.Format(t.layout))
	}
}

// 当前时间
func Now() Time {
	return Time{Time: sysTime.Now()}
}

// 根据字符解析时间
func Parse(layout, value string) (time Time, err error) {
	t, err := sysTime.ParseInLocation(layout, value, TimeZone)
	if err != nil {
		return
	}
	time.Time = t
	return
}
